package ch.ethz.fcl.metrobuzz.algorithm.database;

import java.util.ArrayList;
import java.util.List;

import ch.ethz.fcl.metrobuzz.MBContainer;
import ch.ethz.fcl.metrobuzz.algorithm.list.ListOperation;
import ch.ethz.fcl.metrobuzz.data.long_term_trajectory.node.LTTNode;
import ch.ethz.fcl.metrobuzz.data.long_term_trajectory.node.LTTNodeInterchange;
import ch.ethz.fcl.metrobuzz.render.interchange.inter.InterInterchangeRender;
import ch.ethz.fcl.metrobuzz.render.techniques.circos.CircosDiagram;
import ch.ethz.fcl.metrobuzz.render.techniques.colors.ColorTable;

public class AssociationNodeTransactionList {
	private List<LTTNode> nodeList;
	private List<Integer> transIndexList;
	List<LTTNodeInterchange> interchanges = null;

	public AssociationNodeTransactionList() {
		init(null);
	}

	public AssociationNodeTransactionList(List<LTTNode> list) {
		init(list);
	}

	private void init(List<LTTNode> nodes) {
		this.nodeList = new ArrayList<LTTNode>();
		this.transIndexList = new ArrayList<Integer>();
		if (nodes != null)
			nodeList.addAll(nodes);
	}

	public void addNode(LTTNode node) {
		if (nodeList.contains(node))
			return;
		nodeList.add(node);
	}

	public void addNode(LTTNode node, int index) {
		if (nodeList.contains(node))
			return;
		if (index == -1)
			nodeList.add(node);
		else
			nodeList.add(index, node);
	}

	public void addTransIndex(int index) {
		transIndexList.add(index);
	}

	public void addTransIndices(List<Integer> indices) {
		transIndexList.addAll(indices);
	}

	public void render(MBContainer container, LTTNode selectedNode,
			int index) {
		CircosDiagram cd = selectedNode.getInterchange().getCircos();
		float maxValue = cd.getData().getTotalSum();
		double angle = Math.PI * frequency() / maxValue;
		float width = (float) (2 * Math.sin(angle / 2) * cd.getRadius());

		if (interchanges == null)
			initInterchanges();

		float[] color = ColorTable.getRGBColor(index, 1.0f);
		InterInterchangeRender.render(container.getViewer().getProjection(),
				interchanges, width, color);
	}

	private void initInterchanges() {
		interchanges = new ArrayList<LTTNodeInterchange>();
		for (LTTNode node : nodeList)
			interchanges.add(node.getInterchange());
	}

	public int size() {
		return nodeList.size();
	}

	public List<LTTNode> getNodeList() {
		return this.nodeList;
	}

	public List<Integer> getTransIndexList() {
		return this.transIndexList;
	}

	public int frequency() {
		return transIndexList.size();
	}

	public String toString() {
		String output = "";
		int i = 0;
		for (; i < nodeList.size() - 1; i++)
			output += nodeList.get(i).toNode().getId() + "->";
		output += nodeList.get(i).toNode().getId() + ": " + frequency();
		return output;
	}

	@Override
	public boolean equals(Object o) {
		if (!(o instanceof AssociationNodeTransactionList))
			return false;

		AssociationNodeTransactionList tl = (AssociationNodeTransactionList) o;
		if (size() != tl.size())
			return false;

		for (int i = 0; i < size(); i++)
			if (!nodeList.get(i).equals(tl.nodeList.get(i)))
				return false;
		return true;
	}

	public boolean contains(AssociationNodeTransactionList anotherList) {
		if (anotherList.size() >= size())
			return false;
		int firstTransNodeIndex = nodeList.indexOf(anotherList.getNodeList()
				.get(0));

		if (firstTransNodeIndex == -1)
			return false;

		if (anotherList.size() + firstTransNodeIndex > size())
			return false;

		for (int i = 0; i < anotherList.size(); i++)
			if (!nodeList.get(i + firstTransNodeIndex).equals(
					anotherList.getNodeList().get(i)))
				return false;
		return true;
	}

	public AssociationNodeTransactionList combines(
			AssociationNodeTransactionList antl2) {
		AssociationNodeTransactionList antl1 = this;

		if (antl1 == null || antl1.size() == 0 || antl2 == null
				|| antl2.size() == 0)
			return null;

		AssociationNodeTransactionList a1 = antl1, a2 = antl2;
		if (a2.getNodeList().indexOf(a1.getNodeList().get(0)) != -1) {
			a1 = antl2;
			a2 = antl1;
		}

		int size1 = a1.size(), size2 = a2.size();

		int a2StartInA1 = a1.getNodeList().indexOf(a2.getNodeList().get(0));
		if (a2StartInA1 == -1)
			return null;

		int a1EndInA2 = a2.getNodeList().indexOf(
				a1.getNodeList().get(size1 - 1));
		if (a1EndInA2 == -1 || a1EndInA2 == 0)
			return null;

		if (a2.getNodeList().indexOf(a1.getNodeList().get(size1 - 1)) == -1)
			return null;

		if (size2 < size1 - a2StartInA1)
			return null;

		AssociationNodeTransactionList combine = new AssociationNodeTransactionList();
		for (int i = 0; i < size1; i++) {
			if (i >= a2StartInA1
					&& !a1.getNodeList().get(i)
							.equals(a2.getNodeList().get(i - a2StartInA1)))
				return null;
			combine.addNode(a1.getNodeList().get(i));
		}

		for (int i = a1EndInA2 + 1; i < size2; i++)
			combine.addNode(a2.getNodeList().get(i), -1);

		combine.addTransIndices(ListOperation.unionWithoutDuplicates(
				antl1.getTransIndexList(), antl2.getTransIndexList()));
		return combine;
	}
}
